<template>
  <view>
    <view :class="['date', type]" :style="{ borderColor, opacity: disabled ? 0.6 : 1, height }" @tap="tapInput">
      <slot name="icon">
        <view v-if="showIcon" class="calendar" :style="{ borderColor: iconColor }">
          <view class="calendar-top calendar-top-left" :style="{ background: iconColor }" />
          <view class="calendar-top calendar-top-right" :style="{ background: iconColor }" />
          <view class="calendar-line" :style="{ background: iconColor }" />
          <view class="calendar-line calendar-line-bottom" :style="{ background: iconColor }" />
        </view>
      </slot>
      <text class="date-text" :style="{ color: date ? color : placeholderColor, fontSize, textAlign }">
        {{ date || placeholder }}
      </text>
      <slot name="arrow">
        <view v-if="showArrow && (!showClear || showClear && !date)" class="arrow-icon" :style="{ boxShadow: `-4rpx 4rpx 0 0rpx ${arrowColor} inset` }" />
      </slot>
      <view v-if="showClear && date" class="el-clear" @click.stop="clear">
        <slot name="clear">
          <view class="el-clear-icon" :style="{ background: clearColor }">
            <view class="clear-line" :style="{ background: clearColor }" />
          </view>
        </slot>
      </view>
    </view>

    <view v-if="visible" class="el-mask" @tap="visible = false"></view>

    <view v-if="visible" class="picker-con" :style="{ background: pickerBg, transform: visible ? 'translateY(0)' : 'translateY(100%)' }">
      <view class="flex">
        <view @tap="visible = false">
          <slot name="cancel">
            <view class="el-btn text-cancel" :style="{ color: cancelColor }">取消</view>
          </slot>
        </view>
        <view style="display: flex;align-items: center;">
          <slot name="title">
            <view class="text-title" :style="{ color: titleColor }">{{ title }}</view>
          </slot>
        </view>
        <view @tap="confirm">
          <slot name="confirm">
            <view class="el-btn text-confirm" :style="{ color: confirmColor }">确认</view>
          </slot>
        </view>
      </view>
      <picker-view
        class="picker-view"
        :indicator-style='indicatorStyle'
        :mask-style='maskStyle'
        :value="pickerVal"
        indicator-class="el-datetime-picker"
        mask-class='el-datetime-picker-mask'
        @change="bindChange"
        @touchmove="touchmove"
        @touchend="touchend"
      >
        <picker-view-column>
          <view v-for="(item, index) in years" :key="index" class="item" :style="[{ color: index === pickerVal[0] ? activeColor : pickerColor }, itemStyle]">
            {{ item }}年
          </view>
        </picker-view-column>
        <picker-view-column>
          <view v-for="(item, index) in months" :key="index" class="item" :style="[{ color: index === pickerVal[1] ? activeColor : pickerColor }, itemStyle]">
            {{ item }}月
          </view>
        </picker-view-column>
        <picker-view-column>
          <view v-for="(item, index) in days" :key="index" class="item" :style="[{ color: index === pickerVal[2] ? activeColor : pickerColor }, itemStyle]">
            {{ item }}日
          </view>
        </picker-view-column>
        <block v-if="mode === 'dateTime'">
          <picker-view-column>
            <view v-for="(item, index) in hours" :key="index" class="item" :style="[{ color: index === pickerVal[3] ? activeColor : pickerColor }, itemStyle]">
              {{ item }}时
            </view>
          </picker-view-column>
          <picker-view-column>
            <view v-for="(item, index) in minutes" :key="index" class="item" :style="[{ color: index === pickerVal[4] ? activeColor : pickerColor }, itemStyle]">
              {{ item }}分
            </view>
          </picker-view-column>
        </block>
      </picker-view>
      <view v-if="showCover" class="cover"></view>
    </view>
  </view>
</template>

<script lang="js">
import dayjs from 'dayjs'
export default {
  name: 'ElDateTimePicker',
  props: {
    title: {
      type: String,
      default: '请选择日期'
    },
    titleColor: {
      type: String,
      default: 'rgba(0,0,0,0.80)'
    },
    mode: {
      type: String,
      default: 'date'
    },
    start: {
      type: [String, Number],
      default: '1900-01-01'
    },
    end: {
      type: [String, Number],
      default: '2050-12-31'
    },
    value: {
      type: [String, Number],
      default: ''
    },
    disabled: {
      type: Boolean,
      default: false
    },
    type: {
      type: String,
      default: 'none' // border,none,border-bottom
    },
    height: {
      type: String,
      default: '66rpx'
    },
    fontSize: {
      type: String,
      default: '30rpx'
    },
    color: {
      type: String,
      default: '#333'
    },
    textAlign: {
      type: String,
      default: 'left'
    },
    borderColor: {
      type: String,
      default: '#ddd'
    },
    showIcon: {
      type: Boolean,
      default: false
    },
    iconColor: {
      type: String,
      default: '#555'
    },
    showArrow: {
      type: Boolean,
      default: true
    },
    arrowColor: {
      type: String,
      default: 'rgba(0,0,0,0.25)'
    },
    showClear: {
      type: Boolean,
      default: false
    },
    clearColor: {
      type: String,
      default: '#999'
    },
    placeholder: {
      type: String,
      default: '请选择日期'
    },
    placeholderColor: {
      type: String,
      default: 'rgba(0,0,0,0.25)'
    },
    pickerBg: {
      type: String,
      default: '#fff'
    },
    cancelColor: {
      type: String,
      default: 'rgba(0, 0, 0, 0.4)'
    },
    confirmColor: {
      type: String,
      default: '#c70e2d'
    },
    maskStyle: {
      type: String,
      default: ''
    },
    indicatorStyle: {
      type: String,
      default: 'height: 50px;'
    },
    pickerColor: {
      type: String,
      default: ''
    },
    activeColor: {
      type: String,
      default: ''
    },
    returnType: {
      type: String,
      default: 'string'
    }
  },
  emits: ['change', 'update:value'],
  data () {
    const baseArr = ['', '']
    return {
      date: '',
      years: baseArr,
      months: baseArr,
      days: baseArr,
      hours: baseArr,
      minutes: baseArr,
      days28: [],
      pickerVal: [],
      visible: false,
      itemStyle: null,
      startObj: {
        months: [],
        days: []
      },
      endObj: {
        months: [],
        days: []
      },
      changeDisabled: false,
      moveFlag: false,
      showCover: false,
      hasChange: false
    }
  },
  watch: {
    start () {
      this.setStartEnd()
      const date = this.value ? this.value : new Date()
      this.initCurr(date)
    },
    end () {
      this.setStartEnd()
      const date = this.value ? this.value : new Date()
      this.initCurr(date)
    },
    value (val) {
      const date = val ? val : new Date()
      this.initCurr(date)
      const { y, m, d, h, minu } = this.trimDate(this.value)
      const trimN = this.trimNum
      if (this.mode === 'date') {
        this.date = `${y}-${trimN(m)}-${trimN(d)}`
      } else {
        this.date = `${y}-${trimN(m)}-${trimN(d)} ${trimN(h)}:${trimN(minu)}`
      }
    }
  },
  created () {
    this.days28 = this.setArray(1, 28)
    if (this.activeColor || this.pickerColor) {
      this.itemStyle = {
        height: '50px',
        lineHeight: '50px'
      }
    }

    this.setStartEnd()
    if (this.value) {
      const { y, m, d, h, minu } = this.trimDate(this.value)
      const trimN = this.trimNum
      if (this.mode === 'date') {
        this.date = `${y}-${trimN(m)}-${trimN(d)}`
      } else {
        this.date = `${y}-${trimN(m)}-${trimN(d)} ${trimN(h)}:${trimN(minu)}`
      }
    }
    const date = this.value ? this.value : new Date()
    this.initCurr(date)
  },
  methods: {
    // 点击显示框
    tapInput () {
      if (!this.disabled) {
        this.visible = !this.visible
        // #ifdef MP-BAIDU
        if (this.visible) {
          const val = this.pickerVal
          this.pickerVal = [-5, -5, -5, -5, -5]
          setTimeout((_) => {
            this.pickerVal = val
          })
        }
        // #endif
      }
    },
    // 设置一个是否是touchmove的flag
    touchmove () {
      this.moveFlag = true
    },
    touchend () {
      // touchend时，如果有move，则显示遮罩，以防多次滑动
      if (this.moveFlag) {
        this.showCover = true
        this.moveFlag = false
      }
      // move没有触发change事件，则隐藏遮罩
      setTimeout((_) => {
        if (!this.hasChange) {
          this.showCover = false
        }
      }, 200)
    },
    // 当滚动选择
    bindChange: function (e) {
      if (this.changeDisabled) return

      this.changeDisabled = true
      const prev = this.pickerVal
      const val = e.detail.value
      const y = this.years[val[0]] ? this.years[val[0]] : this.years[this.years.length - 1]
      const m = this.months[val[1]] ? this.months[val[1]] : this.months[this.months.length - 1]
      const d = this.days[val[2]] ? this.days[val[2]] : this.days[this.days.length - 1]
      const h = this.hours[val[3]] >= 0 ? this.hours[val[3]] : this.hours[this.hours.length - 1]
      const minu = this.minutes[val[4]] >= 0 ? this.minutes[val[4]] : this.minutes[this.minutes.length - 1]

      const date = `${y}/${m}/${d} ${h}:${minu}`
      if (prev[1] !== val[1]) {
        this.initCurr(date, m, d)
      } else {
        this.initCurr(date)
      }
      this.pickerVal = val
    },
    // 点击确定
    confirm () {
      const val = this.pickerVal
      const trimN = this.trimNum
      let date = ''
      const d = `${this.years[val[0]]}-${trimN(this.months[val[1]])}-${trimN(this.days[val[2]])}`
      const t = `${trimN(this.hours[val[3]])}:${trimN(this.minutes[val[4]])}`
      if (this.mode === 'date') {
        date = `${d}`
      } else {
        date = `${d} ${t}`
      }
      this.date = date
      let rV = date
      if (this.returnType === 'timestamp') {
        rV = new Date(date).getTime()
      } else if (this.returnType === 'date') {
        rV = new Date(date)
      }
      if (this.mode === 'date') {
        this.$emit('change', dayjs(rV).format('YYYY-MM-DD'))
        this.$emit('update:value', dayjs(rV).format('YYYY-MM-DD'))
      } else {
        this.$emit('change', rV)
        this.$emit('update:value', rV)
      }
      this.visible = false
    },
    clear () {
      this.date = ''
      this.$emit('change', '')
    },
    // 根据年月,设置日期(不同月份，日期不同)
    setDays (year, month, start) {
      let newDays = []
      const monthReg = /^4|6|9|11$/
      let days = this.days28
      if (start) {
        days = this.setArray(start, 28)
      }
      if (monthReg.test(month)) {
        newDays = [...days, 29, 30]
      } else if (month === 2) {
        if ((year % 4 === 0 && year % 100 !== 0) || year % 400 === 0) {
          newDays = [...days, 29]
        } else {
          newDays = days
        }
      } else {
        newDays = [...days, 29, 30, 31]
      }
      return newDays
    },
    // 设置最大最小月日时分
    setStartEnd () {
      const { sy, ey, sm, em, sd, ed, sh, eh, sminu, eminu } = this.getSE()

      this.years = this.setArray(sy, ey)
      // 设置最大最小月
      if (sy === ey) {
        this.startObj.months = this.endObj.months = this.setArray(sm, em)
      } else {
        this.startObj.months = this.setArray(sm, 12)
        this.endObj.months = this.setArray(1, em)
      }

      // 设置最大最小日
      if (sy === ey && sm === em) {
        this.startObj.days = this.endObj.days = this.setArray(sd, ed)
      } else {
        this.startObj.days = this.setDays(sy, sm, sd)
        this.endObj.days = this.setArray(1, ed)
      }

      // 设置最大最小时
      if (sy === ey && sm === em && sd === ed) {
        this.startObj.hours = this.endObj.hours = this.setArray(sh, eh)
      } else {
        this.startObj.hours = this.setArray(sh, 23)
        this.endObj.hours = this.setArray(0, eh)
      }

      // 设置最大最小分钟
      if (sy === ey && sm === em && sd === ed && sh === eh) {
        this.startObj.minutes = this.endObj.minutes = this.setArray(sminu, eminu)
      } else {
        this.startObj.minutes = this.setArray(sminu, 59)
        this.endObj.minutes = this.setArray(0, eminu)
      }
    },
    // 初始化选中项
    initCurr (val, month, day) {
      // console.log(val)
      this.hasChange = true
      const { start, end, sy, ey, sm, em, sd, ed, sh, eh, sminu, eminu } = this.getSE()
      const { val: curr, y: cy, m: cm, d: cd, h: ch, minu: cminu } = this.trimDate(val, month, day)

      let cyi = this.years.indexOf(cy)
      let cmi = 0
      let cdi = 0
      let chi = 0
      let cminui = 0

      let endSIFlag = true // 是否在最后统一设置index

      if (curr.getTime() >= end.getTime()) {
        //  值大于等于结束时间
        // console.log(11)
        this.months = this.endObj.months
        this.days = this.endObj.days
        this.hours = this.endObj.hours
        this.minutes = this.endObj.minutes
        cyi = this.years.length - 1
        cmi = this.months.length - 1
        cdi = this.days.length - 1
        chi = this.hours.length - 1
        cminui = this.minutes.length - 1
        endSIFlag = false
      } else if (curr.getTime() <= start.getTime()) {
        // 值小于等于开始时间
        // console.log(22)
        this.months = this.startObj.months
        this.days = this.startObj.days
        this.hours = this.startObj.hours
        this.minutes = this.startObj.minutes
        cyi = cmi = cdi = chi = cminui = 0
        endSIFlag = false
      } else if (cy === ey && cm === em && cd === ed && ch === eh) {
        // 值与结束时间年/月/日/时相同
        // console.log(222)
        this.months = this.endObj.months
        this.days = this.endObj.days
        this.hours = this.endObj.hours
        this.minutes = this.endObj.minutes
      } else if (cy === ey && cm === em && cd === ed) {
        // 值与结束时间年/月/日相同
        // console.log(111)
        this.months = this.endObj.months
        this.days = this.endObj.days
        this.hours = this.endObj.hours
        // console.log(this.hours)
        this.minutes = this.setArray(0, 59)
      } else if (cy === ey && cm === em) {
        // 值与结束时间年、月相同
        // console.log(33)
        this.months = this.endObj.months
        this.days = this.endObj.days
        this.hours = this.setArray(0, 23)
        this.minutes = this.setArray(0, 59)
      } else if (cy === ey) {
        // 值与结束时间年份相同
        // console.log(44)
        this.months = this.endObj.months
        this.days = this.setDays(cy, cm)
        this.hours = this.setArray(0, 23)
        this.minutes = this.setArray(0, 59)
      } else if (cy === sy && cm === sm && cd === sd && ch === sh) {
        // 值与开始时间年/月/日/时相同
        // console.log(99)
        this.months = this.startObj.months
        this.days = this.startObj.days
        this.hours = this.startObj.hours
        this.minutes = this.startObj.minutes
      } else if (cy === sy && cm === sm && cd === sd) {
        // 值与开始时间年/月/日相同
        // console.log(88)
        this.months = this.startObj.months
        this.days = this.startObj.days
        this.hours = this.startObj.hours
        this.minutes = this.setArray(0, 59)
      } else if (cy === sy && cm === sm) {
        // 值与开始时间年、月相同
        // console.log(55)
        this.months = this.startObj.months
        this.days = this.startObj.days
        this.hours = this.setArray(0, 23)
        this.minutes = this.setArray(0, 59)
      } else if (cy === sy) {
        // 值与开始时间年份相同
        // console.log(66)
        this.months = this.startObj.months
        this.days = this.setDays(cy, cm)
        this.hours = this.setArray(0, 23)
        this.minutes = this.setArray(0, 59)
      } else {
        // 值与开始时间、结束时间不同年月
        // console.log(77)
        this.months = this.setArray(1, 12)
        this.days = this.setDays(cy, cm)
        this.hours = this.setArray(0, 23)
        this.minutes = this.setArray(0, 59)
      }

      if (endSIFlag) {
        const di = this.days.indexOf(cd)

        cmi = this.months.indexOf(cm)
        cdi = di === -1 ? this.days.length - 1 : di
        chi = this.hours.indexOf(ch)
        cminui = this.minutes.indexOf(cminu)
      }

      this.$nextTick((_) => {
        if (this.mode === 'date') {
          this.pickerVal = [cyi, cmi, cdi]
        } else {
          this.pickerVal = [cyi, cmi, cdi, chi, cminui]
        }
        this.changeDisabled = false
        this.showCover = false
        this.hasChange = false
      })
    },
    // 设置开始/结束时间相关变量
    getSE () {
      let s = this.start
      let e = this.end
      s = s ? s : '1900-01-01 00:00'
      e = e ? e : '2050-12-31 23:59'
      const { val: start, y: sy, m: sm, d: sd, h: sh, minu: sminu } = this.trimDate(s)
      const { val: end, y: ey, m: em, d: ed, h: eh, minu: eminu } = this.trimDate(e)
      return {
        start,
        sy,
        sm,
        sd,
        sh,
        sminu,
        end,
        ey,
        em,
        ed,
        eh,
        eminu
      }
    },
    // 返回时间信息
    trimDate (val, month, day) {
      const reg = /^\d{4}-\d{1,2}-\d{1,2}( \d{1,2}:\d{1,2})?(:\d{1,2})?$/
      if (typeof val === 'string' && reg.test(val)) {
        val = val.replace(/-/g, '-')
      }
      const curr = new Date(val)

      return {
        val: curr,
        y: curr.getFullYear(),
        m: month ? month : curr.getMonth() + 1,
        d: day ? day : curr.getDate(),
        h: curr.getHours(),
        minu: curr.getMinutes()
      }
    },
    // 获取一个范围内的整数，返回数组
    setArray (start, end) {
      const arr = []
      for (let i = start; i <= end; i++) {
        arr.push(i)
      }
      return arr
    },
    // 将数字转换成两位
    trimNum (num) {
      num = num >= 10 ? num : `0${num}`
      return num
    }
  }
}
</script>

<style lang="scss" scoped>
.text-cancel,
.text-confirm {
  font-size: 28rpx;
  line-height: 28rpx;
  font-weight: 400;
}
.text-title {
  font-size: 32rpx;
  color: rgba(0, 0, 0, 0.8);
  text-align: center;
  line-height: 32rpx;
  font-weight: 500;
}
.date {
  /* padding: 16rpx; */
  font-size: 28rpx;
  display: flex;
  align-items: center;
  box-sizing: border-box;
}
.date-text {
  flex: 1;
}
.border {
  border: 1rpx solid;
  border-radius: 6rpx;
}
.border-bottom {
  border-bottom: 1rpx solid;
}
.calendar {
  width: 30rpx;
  height: 30rpx;
  border: 1rpx solid;
  border-radius: 4rpx;
  margin-right: 16rpx;
  margin-top: 6rpx;
  position: relative;
}
.calendar .calendar-line {
  width: 26rpx;
  height: 4rpx;
  position: absolute;
  left: 2rpx;
  top: 10rpx;
  opacity: 0.6;
  z-index: 1;
}
.calendar .calendar-line-bottom {
  top: 20rpx;
}
.calendar .calendar-top {
  position: absolute;
  top: -6rpx;
  width: 4rpx;
  height: 12rpx;
  z-index: 2;
  border-radius: 2rpx;
}
.calendar .calendar-top-left {
  left: 6rpx;
}
.calendar .calendar-top-right {
  right: 6rpx;
}
.arrow-icon {
  width: 24rpx;
  height: 24rpx;
  transform: rotate(45deg);
  margin-left: 20rpx;
  margin-right: 8rpx;
}
.el-clear {
  min-width: 40rpx;
  min-height: 40rpx;
  display: flex;
  align-items: center;
  justify-content: center;
}
.el-clear-icon {
  position: relative;
  width: 30rpx;
  height: 4rpx;
  transform: rotate(45deg);
  border-radius: 4em;
}
.el-clear-icon .clear-line {
  position: absolute;
  width: 30rpx;
  height: 4rpx;
  transform: rotate(90deg);
  border-radius: 4em;
}
.el-mask {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  background-color: rgba(0, 0, 0, 0.4);
  animation: dropdown1 0.2s linear;
}
.picker-con {
  width: 750rpx;
  border-top-right-radius: 30rpx;
  border-top-left-radius: 30rpx;
  overflow: hidden;
  // box-shadow: 0 0 20rpx 0 rgba(0, 0, 0, .4);
  position: absolute;
  bottom: 0;
  left: 0;
  background-color: #fff;
  z-index: 999;
  transition: all 0.3s;
  animation: dropdown 0.2s linear;
}
.flex {
  display: flex;
  flex-direction: row;
  justify-content: space-between;
}
.el-btn {
  width: 120rpx;
  padding: 26rpx 20rpx;
  text-align: center;
}
.picker-view {
  width: 750rpx;
  height: 500rpx;
}
.picker-view .item {
  line-height: 50px;
  align-items: center;
  justify-content: center;
  text-align: center;
}
.cover {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
}
@keyframes dropdown {
  0% {
    height: 0;
  }
  100% {
    height: 500rpx;
  }
}
@keyframes dropdown1 {
  0% {
    opacity: 0;
  }
  100% {
    opacity: 1;
  }
}
</style>
